<template>
  <div class="container">
    <div style="margin-bottom: 20px;">docx2html浏览器版无法处理带有页眉页脚的文档</div>
    <div class="top-controls">
      <input type="file" @change="handleFileUpload" accept=".docx" />
      <el-button style="margin-left: 10px; padding: 4px 8px" @click="exportToWord">导出为Word</el-button>
    </div>
    <div class="main-content">
      <div class="left-panel">
        <div>
          <el-form v-model="formObj">
            <el-form-item label="文档标题">
              <el-input v-model="formObj.label" placeholder="请输入文档标题"></el-input>
              <el-button @click="handleJump('label')">跳转</el-button>
            </el-form-item>
            <el-form-item label="文档描述">
              <el-input v-model="formObj.description" placeholder="请输入文档描述"></el-input>
            </el-form-item>
          </el-form>
        </div>
        <h3>文档目录</h3>
          <el-tree
          :data="treeData"
          :props="treeProps"
          default-expand-all
          @node-click="handleNodeClick"
          highlight-current
          :expand-on-click-node="false"
          node-key="id"
          class="doc-tree"
        >
          <template #default="{ node, data }">
            <div class="custom-tree-node">
              <span>{{ node.label }}</span>
              <div v-if="data.id >= 0 && data.level < 6">
                <el-icon @click="append(data)"><CirclePlus /></el-icon>
              </div>
            </div>
          </template>
        </el-tree>
      </div>
      <div class="right-panel">
        <el-button @click="handleSwitch">替换</el-button>
        <div id="toolbar"></div>
        <div id="editor">
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref,onMounted, nextTick, watch, unref, reactive } from 'vue';
import docx2html from 'docx2html';
import mammoth from 'mammoth';
import { saveAs } from "file-saver";
import 'element-plus/dist/index.css';
import { ElTree, ElEmpty, ElButton,
  ElIcon, ElMessage, ElMessageBox, ElForm, ElFormItem, ElInput} from 'element-plus';
import Quill from 'quill';
// import ImageResize from 'quill-image-resize';
import QuillResizeImage from 'quill-resize-image';
import { debounce }  from 'lodash';
import {CirclePlus} from '@element-plus/icons-vue';
import "quill-mention/autoregister";
import QuillTableBetter from 'quill-table-better';
import 'quill-table-better/dist/quill-table-better.css'

let quill;
const treeData = ref([]);
const currentNode = ref(null);
const currentContent = ref('');
const treeProps = {
  children: 'children', // 指定子节点字段名
  label: 'label',       // 指定标签字段名
  isLeaf: 'isLeaf'      // 指定是否为叶节点的字段名
};

const formObj = reactive({
  label: '安彦辉',
  description: '安达维尔',
});
const labelMap = {
  label: '文档标题',
  description: '文档描述',
}
// 生成跟变量相关的结构化模板
function generateTemplate(key, value) { 
  return `<span class="mention" 
    data-index="0" data-denotation-char="" data-id="${key}" 
    data-value="${value}"><span contenteditable="false"><span class="ql-mention-denotation-char"></span>
    <span class="ql-mention-value">${value}</span></span></span>`
}

const atValues = ref([])
watch(() => formObj, debounce((value) => {
 const keys = Object.keys(value);
 if (keys.length > 0) {
   // 把值存到atValues中
   atValues.value = keys.map(key => ({ id: key, value: value[key] }));
 }
}), {
  deep: true,
  immediate: true,
})
const handleSwitch = () => {
 let htmlContent = getQuillContentAsHtml();
 for (let i = 0; i < atValues.value.length; i++) {
   const item = atValues.value[i];
   htmlContent = replaceMentionByKey(htmlContent, item.id, item.value);
  //  console.log(htmlContent)
  quill.setContents([{ insert: '\n' }]);
  quill.clipboard.dangerouslyPasteHTML(0, htmlContent);
 }
}

// 替换函数
function replaceMentionByKey(html, key, value) {
    // 构建带参数的正则表达式，转义key中的特殊字符以确保正确匹配
    const regex = new RegExp(
        '<span class="mention"[^>]*data-id="' + 
        key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + 
        '"[^>]*>(.*?)<span class="ql-mention-value">([^<]*)</span>', 
        'gi'
    );
    
    return html.replace(regex, function(match, middleContent, currentText) {
        // 替换data-value属性（如果存在）或添加该属性
        const newDataValue = match.includes('data-value') 
            ? match.replace(/data-value="[^"]*"/, `data-value="${value}"`) 
            : match.replace('</span>', ` data-value="${value}"></span>`);
        
        // 替换ql-mention-value标签内的文本
        return newDataValue.replace(
            `<span class="ql-mention-value">${currentText}</span>`, 
            `<span class="ql-mention-value">${value}</span>`
        );
    });
}
// 跳转到制定的位置
let clickNum = 0;
let oldLabel = '';
function handleJump(label) {
  // 处理切换字段的逻辑
  if (oldLabel !== label) {
    oldLabel = label;
    clickNum = 0;
  }
  // 获取所有标题元素
  const linkNodes = Array.from(document.querySelectorAll('.mention[data-id="'+label+'"]'));
  if (linkNodes.length === 0) {
    return; 
  } else if (clickNum > linkNodes.length - 1) {
    clickNum = 0;
    return; 
  } else {
    clickNum++;
    const linkNode = linkNodes[clickNum - 1];
    // const linkBlot = Quill.find(linkNode);
    // let index = quill.getIndex(linkBlot);
    // console.log(index);
    linkNode.scrollIntoView({ behavior: 'smooth', block: 'center' });
    // linkNode.scrollTo({
    //   top: index * 13, // 减去50px的偏移量
    //   behavior:'smooth' // 平滑滚动
    // });
  }
}

onMounted(() => { 
  Quill.register({
    'modules/table-better': QuillTableBetter
  }, true);
  const ImageBlot = Quill.import('formats/image');
  class CustomImageBlot extends ImageBlot {
      static create(value) {
        const node = super.create(value);
        if (typeof value === 'string') {
          // 提取并应用原始样式
          const parser = new DOMParser();
          const doc = parser.parseFromString(value, 'text/html');
          const img = doc.querySelector('img');
          if (img && img.style) {
            node.setAttribute('style', img.style.cssText);
          }
        }
        return node;
      }
      
      static formats(domNode) {
        // 返回图片的样式属性，确保 Quill 不会过滤它们
        return {
          style: domNode.getAttribute('style')
        };
      }
      
      format(name, value) {
        if (name === 'style' && value) {
          this.domNode.setAttribute(name, value);
        } else {
          super.format(name, value);
        }
      }
    }
  // 注册自定义 Image blot
  Quill.register('formats/image', CustomImageBlot, true);
  const toolbarOptions = [
    ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
    ['blockquote', 'code-block'],
    ['link', 'image', 'video', 'formula'],

    [{ 'list': 'ordered'}, { 'list': 'bullet' }, { 'list': 'check' }],
    [{ 'script': 'sub'}, { 'script': 'super' }],      // superscript/subscript
    [{ 'indent': '-1'}, { 'indent': '+1' }],          // outdent/indent
    [{ 'direction': 'rtl' }],                         // text direction

    [{ 'header': [1, 2, 3, 4, 5, 6, false] }],

    [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme
    [{ 'font': [] }],
    [{ 'align': [] }],
    ['table-better'],
    ['clean'],                                        // remove formatting button
  ];
  // Quill.register('modules/imageResize', ImageResize);
  Quill.register("modules/resize", QuillResizeImage);
  
  quill = new Quill('#editor', {
    theme: 'snow',
    modules:{
      toolbar: toolbarOptions,
      clipboard: {
        matchers: [
          // 匹配 img 标签，保留 style 属性
          ['img', (node, delta) => {
            const style = node.getAttribute('style');
            if (style) {
              delta.ops[0].attributes = delta.ops[0].attributes || {};
              delta.ops[0].attributes.style = style;
            }
            return delta;
          }]
        ]
      } ,
      // imageResize: {
      //   // parchment: Quill.import('parchment'),
      //   modules: ['Resize', 'DisplaySize', 'Toolbar'],
      //   handleStyles: {
      //       backgroundColor: 'black',
      //       border: 'none',
      //       color: "#ffffff"
      //       // other camelCase styles for size display
      //   }
      // } ,
      resize: {
        locale: {},
      },
      mention: {
        allowedChars: /^[A-Za-z\sÅÄÖåäö]*$/,
        mentionDenotationChars: ["@"], 
        allowInlineMentionChar: true,
        showDenotationChar: false, // 是否显示@符号
        source: function(searchTerm, renderList, mentionChar) {
          var values;
          if (mentionChar === "@") {
            values = atValues.value; 
          }
          if (searchTerm.length === 0) {
            renderList(values, searchTerm); 
          }
          else {
            var matches = [];
            for (var i = 0; i < values.length; i++) {
              if (~values[i].toLowerCase().indexOf(searchTerm.toLowerCase())) {
                matches.push(values[i]);
              }
            } 
            renderList(matches, searchTerm);
          }
        },
        renderItem: function(item) {
          // 这里显示 ID，但实际选择后会插入 value
          return `${labelMap[item.id]} - ${item.value}`;
        },
        insertTrigger: true, // 插入触发后的内容
        onSelect: function(item, insertItem) {
          // 可在此处处理选中事件
          insertItem(item);
        }
      },
      'table-better': {
        language: 'zh_CN',
        menus: ['column', 'row', 'merge', 'table', 'cell', 'wrap', 'copy', 'delete'],
        toolbarTable: true
      },
      keyboard: {
        bindings: QuillTableBetter.keyboardBindings
      }
    },
  });
  // let html = ``;
  // quill.clipboard.dangerouslyPasteHTML(0, html);
  // 监听内容变化并触发保存
  quill.on('text-change', (delta, oldDelta, source) => {
    if (source === 'user') {  // 只处理用户操作
      // 获取编辑器里的内容
      let htmlContent = getQuillContentAsHtml();
      // 进行目录结构的更新
      if (currentNode.value && currentNode.value.label !== '全文') {
        // 更新当前节点的HTML内容
        // todo: 解析htmlContent，更新当前节点的content属性和children属性，同时更新treeData
        currentNode.value.content = htmlContent;
      } else {
        const debouncedFetch = debounce(convertToElTree, 300);
        debouncedFetch(htmlContent)
      }
      // currentNode.value.content = htmlContent;
      // 从treeData中查找到当前节点
      // const node = findNodeInTreeData(currentNode.value, treeData.value);
    }
  });
});
const handleFileUpload = async (e) => {
  quill.setContents([{ insert: '\n' }]);
  treeData.value = [];
  const file = e.target.files[0];
  if (!file) return;

  const reader = new FileReader();
  reader.onload = async (event) => {
    const arrayBuffer = event.target.result;  
    try {
      const result = await docx2html( arrayBuffer );
      let resultContent = result.content.outerHTML;
      // console.log(resultContent)
      // 处理图片并保留尺寸
      resultContent = await processImages1(resultContent);
      
      // 去掉resultContent中<style></style>标签
      resultContent = resultContent.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '');
      
      quill.clipboard.dangerouslyPasteHTML(0, resultContent);
      convertToElTree(resultContent)
    } catch (error) {
      console.error('转换失败:', error);
      console.error('错误堆栈:', error.stack);
    }
  };
  reader.readAsArrayBuffer(file);
};

// 生成目录结构
const convertToElTree = (resultContent) => {
  const titleTree = extractTitleTree(resultContent);
  if (!titleTree) {
    console.error('未找到标题结构');
    return;
  }
  treeData.value = convertToElTreeFormat(titleTree);
};

const append = (data) => {
   ElMessageBox.prompt('请输入标题内容', 'Tip', {
    confirmButtonText: 'OK',
    cancelButtonText: 'Cancel',
  })
    .then(({ value }) => {
      if (data.level >=  6) {
        ElMessage({
          type: 'info',
          message: '标题层级不能超过6级',
        })
      } else {
        let currentLevel = data.level + 1;
        let currentContent = `<h${currentLevel}>${value}</h${currentLevel}>`;
        const newChild = { id: data.id++, level: currentLevel, label: value, children: [] ,content: currentContent}
        if (!data.children) {
          data.children = []
        }
        data.children.push(newChild)
        treeData.value = [...treeData.value]
      }
    })
    .catch(() => {
      ElMessage({
        type: 'info',
        message: 'Input canceled',
      })
    })
};

/**
 * 解析docx2html转换后的内容，提取标题与对应内容（保留格式）
 * @param {string} htmlContent - docx2html转换后的HTML内容
 * @returns {Object} - 包含标题层级和对应内容的对象
 */
 function extractTitleTree(htmlContent) {
    // 定义标题选择器（匹配h1-h3，根据实际HTML结构调整）
    const titleSelectors = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
    const allTitleSelectors = titleSelectors.join(',');
    const maxLevel = titleSelectors.length;
    
    // 创建DOM容器用于解析HTML
    const parser = new DOMParser();
    const doc = parser.parseFromString(htmlContent, 'text/html');
    // 获取所有标题元素
    const titleElements = Array.from(doc.querySelectorAll(allTitleSelectors));
    // 创建全文根节点
    const fullTextNode = {
        id: 0,
        level: 0,
        label: "全文",
        children: [],
        content: '' // 全文内容为整个HTML
    };
    // 如果没有标题，直接返回全文节点
    if (titleElements.length === 0) {
        return fullTextNode;
    }
    // 用于跟踪各层级最后一个标题的引用
    const lastNodes = new Array(maxLevel + 1).fill(null);
    lastNodes[0] = fullTextNode; // 根节点
    // 处理引言内容（如果有且不为空）引言是指在第一个标题之前的内容
    const introContent = extractContentBeforeFirstTitle(htmlContent, titleElements[0]);
    if (introContent.trim()) {
        fullTextNode.children.push({
            id: -1,
            level: 0.5,
            label: "引言",
            children: [],
            content: introContent
        });
    }
    // 处理每个标题元素
    titleElements.forEach((titleEl, index) => {
        const titleLevel = titleSelectors.findIndex(sel => titleEl.matches(sel)) + 1;
        if (titleLevel <= 0) return;
        
        const titleNode = {
            id: index + 1,
            level: titleLevel,
            label: titleEl.textContent.trim(),
            element: titleEl,
            children: [],
            content: ""
        };
        
        // 确定父节点（最近的上级标题）
        let parentLevel = titleLevel - 1;
        let parentNode = lastNodes[parentLevel];
        
        while (!parentNode && parentLevel > 0) {
            parentLevel--;
            parentNode = lastNodes[parentLevel];
        }
        
        parentNode = parentNode || fullTextNode;
        parentNode.children.push(titleNode);
        lastNodes[titleLevel] = titleNode;
        
        // 提取内容（直到下一个任意标题）
        titleNode.content = extractContentUntilNextTitle(titleEl, allTitleSelectors);
    });
    
    return fullTextNode;

}
/**
 * 提取第一个标题之前的内容（引言）
 */
 function extractContentBeforeFirstTitle(htmlContent, firstTitleEl) {
    if (!firstTitleEl) return '';
    
    // 获取第一个标题的outerHTML
    const titleHtml = firstTitleEl.outerHTML;
    
    // 在原始HTML内容中找到第一个标题的位置
    const titleIndex = htmlContent.indexOf(titleHtml);
    
    if (titleIndex === -1) {
        // 无法在原始HTML中找到标题，尝试使用标签选择器
        const titlePattern = /<h1\s+class="\s*a\s+_1\s*"/i;
        const match = htmlContent.match(titlePattern);
        
        if (match) {
            return htmlContent.substring(0, match.index);
        }
        
        // 仍无法找到标题，返回空
        return '';
    }
    
    // 提取标题之前的内容
    const introHtml = htmlContent.substring(0, titleIndex);
    
    // 清理可能的空标签
    // return introHtml.replace(/<[^>]+>\s*<\/[^>]+>/g, '').trim();
    return introHtml.trim();
}

/**
 * 提取标题对应的内容（包含标题自身及其样式，直到下一个任意标题）
 */
 function extractContentUntilNextTitle(titleEl, allTitleSelectors) {
    const contentContainer = document.createElement('div');
    
    // 克隆当前标题元素并添加到内容容器
    const clonedTitle = cloneElement(titleEl);
    contentContainer.appendChild(clonedTitle);
    
    // 提取标题后的内容（直到下一个标题）
    let nextSibling = titleEl.nextSibling;
    
    while (nextSibling) {
        // 如果是任意标题，停止提取
        if (nextSibling.nodeType === 1 && nextSibling.matches(allTitleSelectors)) {
            break;
        }
        
        // 添加到内容容器
        if (nextSibling.nodeType === 1) {
            contentContainer.appendChild(cloneElement(nextSibling));
        }
        
        nextSibling = nextSibling.nextSibling;
    }
    
    return contentContainer.innerHTML;
}

/**
 * 克隆元素及其所有子元素（用于保留格式）
 */
 function cloneElement(element) {
    const clone = element.cloneNode(true);
    
    // 清理不必要的ID（避免冲突）
    if (clone.id) {
        clone.id = '';
    }
    
    // 递归处理子元素
    const children = clone.children;
    for (let i = 0; i < children.length; i++) {
        cloneElement(children[i]);
    }
    
    return clone;
}


/**
 * 将树形结构转换为ElementPlus树组件所需格式（优化版）
 * @param {Object} node - 原始树形结构节点
 * @param {boolean} isRoot - 是否为根节点
 * @returns {Object} - ElementPlus树组件所需格式
 */
 function convertToElTreeFormat(node) {
  // 确保children属性正确处理
  const processChildren = (children) => {
    if (!children || children.length === 0) {
      return undefined; // 无子节点时设为undefined
    }
    
    return children.map(child => ({
      id: child.id,
      label: child.label,
      level: child.level,
      content: child.content,
      isLeaf: !child.children || child.children.length === 0,
      children: processChildren(child.children),
      
      // 自定义属性（通过props配置排除）
      _custom: {
        level: child.level,
        indent: child.level * 20,
        icon: getIconByLevel(child.level)
      }
    }));
  };
  
  // 返回数组格式
  return [{
    id: node.id,
    label: node.label,
    level: node.level,
    content: node.content,
    isLeaf: !node.children || node.children.length === 0,
    children: processChildren(node.children),
    
    // 自定义属性
    _custom: {
      level: node.level,
      indent: node.level * 20,
      icon: getIconByLevel(node.level)
    }
  }];
}


/**
 * 根据标题级别获取对应的图标
 */
function getIconByLevel(level) {
  const icons = ['el-icon-folder-opened', 'el-icon-folder', 'el-icon-document'];
  return icons[Math.min(level - 1, icons.length - 1)] || 'el-icon-document';
}

// 节点点击处理函数
const handleNodeClick = (node) => {
  console.log('点击了节点：', node);
  // 更新当前选中节点
  currentNode.value = node;
  const isFullText = node.label == '全文' && node.children && node.children.length > 0;
  
  // 构建包含当前节点及其所有子节点内容的HTML
  const combinedContent = buildCombinedContent(node, isFullText);
  
  // 更新内容区域
  currentContent.value = combinedContent;
  quill.setContents([{ insert: '\n' }]);
  quill.clipboard.dangerouslyPasteHTML(0, currentContent.value);
};
/**
 * 构建包含当前节点及其所有子节点内容的HTML（修复版）
 * @param {Object} node - 当前节点
 * @returns {string} - 组合后的HTML内容
 */
 function buildCombinedContent(node, isFullText,includeSelf = true) {
  const container = document.createElement('div');
  
  // 添加当前节点的内容（如果需要）
  if (includeSelf && node.content) {
    container.innerHTML = node.content;
  }
  
  // // 递归添加所有子节点的内容
  if (isFullText) {
    const childrenContainer = document.createElement('div');
    childrenContainer.className = 'node-children-content';
    
    node?.children?.forEach(child => {
      // 直接使用子节点的完整内容（已包含标题和内容）
      const childContent = buildCombinedContent(child, isFullText, true);
      childrenContainer.innerHTML += childContent;
    });
    
    if (childrenContainer.innerHTML) {
      container.appendChild(childrenContainer);
    }
  }
  
  return container.innerHTML;
}



const processImages1 = async (html) => {
  const dimensions = extractImageDimensions(html);
  const blobUrls = extractBlobUrls(html); // 提取所有blob URL
  let resultContent = html;
  // 逐个转换为 DataURL
  for (const blobUrl of blobUrls) {
    const response = await fetch(blobUrl);
    const blob = await response.blob();
    // 使用 Promise 封装 FileReader
    const dataUrl = await new Promise((resolve) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.readAsDataURL(blob, { type: 'image/png'});
    });
    // 获取图片尺寸
    const { width, height } = dimensions.get(blobUrl) || {};
    console.log('图片尺寸:', width, height);
    // 构建带尺寸的替换模板
    let replacement = dataUrl;
    if (width || height) {
      const styleAttrs = [];
      if (width) styleAttrs.push(`width:${width.value}px`);
      if (height) styleAttrs.push(`height:${height.value}px`);
      
      // 使用正则替换，保留其他属性
      replacement = `src="${dataUrl}" style="${styleAttrs.join(';')}"`;
    } else {
      replacement = `src="${dataUrl}"`;
    }
    replacement = `src="${dataUrl}"`;
    // 替换 HTML 中的 blob URL 为 DataURL
    resultContent = resultContent.replace(
      new RegExp(`src="(${blobUrl})"`, 'g'), 
      replacement
    );
  }
  return resultContent
}
// 从img的style 中提取宽高信息
function extractImageDimensions(html) {
  const dimensions = new Map();
  const imgRegex = /<img[^>]+src="(blob:[^"]+)"[^>]*>/g;
  
  let match;
  while ((match = imgRegex.exec(html)) !== null) {
    const imgSrc = match[1];
    const imgTag = match[0];
    console.log(imgTag)
    
    // 提取 width/height 信息（支持多种格式）
    const width = extractDimension(imgTag, 'width');
    const height = extractDimension(imgTag, 'height');
    dimensions.set(imgSrc, { width, height });
  }
  
  return dimensions;
}
// 辅助函数：从标签中提取单个维度（宽度或高度）
function extractDimension(imgTag, dimension) {
  // 1. 尝试提取内联样式中的维度 (style="width:100px")
  const styleRegex = new RegExp(`${dimension}\\s*:\\s*([\\d.]+)\\s*(px|%)`, 'i');
  const styleMatch = imgTag.match(styleRegex);
  if (styleMatch) {
    return {
      value: parseFloat(styleMatch[1]),
      unit: styleMatch[2] || 'px'
    };
  }
  
  // 2. 尝试提取属性中的维度 (width="100")
  const attrRegex = new RegExp(`${dimension}\\s*=\\s*["']?([\\d.]+)["']?`, 'i');
  const attrMatch = imgTag.match(attrRegex);
  if (attrMatch) {
    return {
      value: parseFloat(attrMatch[1]),
      unit: 'px' // HTML 属性默认单位为 px
    };
  }
  
  // 3. 未找到维度信息
  return null;
}
// 提取 HTML 中的所有 blob URL
const extractBlobUrls = (html) => {
  const regex = /src="(blob:[^"]+)"/g;
  const matches = [];
  let match;
  while ((match = regex.exec(html)) !== null) {
    matches.push(match[1]);
  }
  return matches;
};

const exportToWord = async () => {

    // const html = quill.getSemanticHTML();
    // if (!html) {
    //   customAlert('编辑器内容为空，无法导出', 'warning');
    //   return;
    // }
    // 预处理表格样式（关键）
    let htmlContent = getQuillContentAsHtml();

    // 把htmlContent中img的src的 data:image/* 转换为  data:image/png

   htmlContent =  fixBase64MimeTypes(htmlContent)
    
      // 构建包含 HTML 内容的完整 HTML 文档字符串
    const fullHtmlContent = `
      <!DOCTYPE html>
      <html>
        <head>
          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        </head>
        <body>
          ${htmlContent}
        </body>
      </html>
    `;
    const blob = htmlDocx.asBlob(fullHtmlContent, {
      orientation: 'portrait',
      // 添加更多选项以符合标准DOCX结构
    });
    saveAs(blob, 'editor-content.docx')
  }
  const fixBase64MimeTypes = (html) => {
    // 匹配data:image/*;base64, 或不规范的MIME类型
    return html.replace(/src="data:image\/(\*|[a-z]+);base64,/g, (match, type) => {
      // 如果是通用类型(*)或不规范类型，根据内容智能判断
      if (type === '*' || !['png', 'jpeg', 'jpg', 'gif', 'webp'].includes(type)) {
        // 这里可以添加更复杂的类型检测逻辑
        // 简化版：默认使用png（最通用的无损格式）
        return 'src="data:image/png;base64,';
      }
      
      // 保留正确的MIME类型
      return match;
    });
  };
  // 将 Quill 内容转换为 HTML
  const getQuillContentAsHtml = () => {
    // 获取 Delta 内容
    const delta = quill.getContents();
    
    // 创建临时容器
    const tempContainer = document.createElement('div');
    
    // 使用 Quill 的 Clipboard 模块转换为 HTML
    const tempQuill = new Quill(tempContainer, { readOnly: true });
    tempQuill.setContents(delta);
    
    // 获取 HTML 内容
    const html = tempContainer.querySelector('.ql-editor').innerHTML;
    // 清理临时容器
    // tempQuill.destroy();
    
    // 处理图片（确保图片为 DataURL 格式）
    let processedHtml = processTableStyles(html);
    processedHtml = processImages(processedHtml);
    // 处理有序列表
    processedHtml = processedHtml.replace(/<ol class="ql-list ql-list-ordered">/g, '<ol style="list-style-type: decimal; padding-left: 20px;">');
  
    // 处理无序列表
    processedHtml = processedHtml.replace(/<ul class="ql-list ql-list-bullet">/g, '<ul style="list-style-type: disc; padding-left: 20px;">');
    processedHtml = convertQuillStylesToInline(processedHtml);
    return processedHtml;
  };

  // 转换 Quill 样式类为内联样式
  const convertQuillStylesToInline = (html) => {
    // 处理文本对齐
    html = html.replace(/class="ql-align-center"/g, 'style="text-align: center;"');
    html = html.replace(/class="ql-align-right"/g, 'style="text-align: right;"');
    html = html.replace(/class="ql-align-justify"/g, 'style="text-align: justify;"');
    
    // 处理字体大小
    html = html.replace(/class="ql-size-small"/g, 'style="font-size: 0.8em;"');
    html = html.replace(/class="ql-size-large"/g, 'style="font-size: 1.2em;"');
    html = html.replace(/class="ql-size-huge"/g, 'style="font-size: 1.5em;"');
    
    // 处理其他样式...
    
    return html;
  };
  // 预处理表格样式
  function processTableStyles(html) {
    // 1. 将表格类转换为内联样式
    html = html.replace(/<table([^>]*)class="([^"]*)"([^>]*)>/gi, (match, before, classes, after) => {
      let inlineStyles = '';
      
      // 处理常用表格类
      if (classes.includes('table-bordered')) {
        inlineStyles += 'border-collapse:collapse;';
      }
      if (classes.includes('table-hover')) {
        inlineStyles += 'background-color:#f5f5f5;';
      }
      
      // 添加到style属性
      if (inlineStyles) {
        if (before.includes('style="')) {
          before = before.replace(/style="([^"]*)"/, `style="$1 ${inlineStyles}"`);
        } else {
          before += ` style="${inlineStyles}"`;
        }
      }
      return `<table${before}${after}>`;
    });
    
    // 2. 确保单元格有边框
    html = html.replace(/<td([^>]*)>/gi, '<td$1 style="border:1px solid #ddd;padding:8px;">');
    html = html.replace(/<th([^>]*)>/gi, '<th$1 style="background-color:#f5f2f0;border:1px solid #ddd;padding:8px;font-weight:bold;border-right-width: 3px;line-height: 1.5;">');
    html = html.replace(/<table([^>]*(?!width)[^>]*?)>/gi, '<table$1 cellspacing="0" cellpadding="0" width="100%" style="border-collapse:collapse;width:100%;table-layout:fixed;">')
      // 情况2：有宽度属性但非100%，替换为100%
      .replace(/<table([^>]*?)width="([^"]+)"([^>]*?)>/gi, '<table$1 cellspacing="0" cellpadding="0" width="100%" style="width:100%;table-layout:fixed;"$3>')
      // 情况3：有style但无宽度，添加宽度
      .replace(/<table([^>]*?)style="([^"]*?)"([^>]*?)>/gi, (match, before, style, after) => {
        if (!style.includes('width')) {
          return `<table${before} cellspacing="0" cellpadding="0" style="${style}width:100%;table-layout:fixed;"${after}>`;
        }
        return match;
      });

    const doc = new DOMParser().parseFromString(html, 'text/html');
    // 自定义隔行换色类
    // const oddClass = 'table-odd';
    // const evenClass = 'table-even';
    const oddBgColor = '#f9f9f9';
    const evenBgColor = '#ffffff';
    // 处理所有包含这些类的表格
    const tables = doc.querySelectorAll(`table`);
    tables.forEach(table => {
      const rows = table.querySelectorAll('tr');
      rows.forEach((row, index) => {
        const isEvenRow = index % 2 === 0;
        const bgColor = isEvenRow ? evenBgColor : oddBgColor;
        
        // 为行内每个单元格设置背景色
        const cells = row.querySelectorAll('td, th');
        cells.forEach(cell => {
          const style = cell.getAttribute('style') || '';
          if (!style.includes('background-color')) {
            cell.setAttribute('style', `${style} background-color: ${bgColor};`);
          }
        });
      });
      
      // 移除表格的自定义类
      // table.classList.remove(oddClass, evenClass);
    });
    return doc.body.innerHTML;
  }

  function processImages(html) {
    const doc = new DOMParser().parseFromString(html, 'text/html');
    
    // 处理所有图片
    const images = doc.querySelectorAll('img[src^="data:image/"]');
    let imageIndex = 1;
    images.forEach(async(img) => {
      // 给每个图下方添加一个p标签，内容为“图一”
      const p = document.createElement('p');
      p.style.textAlign = 'center';
      p.style.FontSize = '12px';
      p.style.color = '#999999';
      p.textContent = `图${imageIndex}`;
      img.parentNode.insertBefore(p, img.nextSibling);
      imageIndex++;
      if (img.style.width) {
        img.setAttribute('width', img.style.width.replace('px', ''));
      }
      // 如果style中高度存在，则使用style中的高度
      if (img.style.height) {
        img.setAttribute('height', img.style.height.replace('px', ''));
      }
      
      // 可选：将图片转换为Base64（确保Word能正确加载）
      if (img.src.startsWith('http') && !img.src.startsWith('data:')) {
        convertImageToBase64(img);
      }
    });
    
    return doc.body.innerHTML;
  }
  // Base64 转 Blob
  const dataUrlToBlob = (dataUrl) => {
    const arr = dataUrl.split(',');
    const mime = arr[0].match(/:(.*?);/)[1];
    const bstr = atob(arr[1]);
    let n = bstr.length;
    const u8arr = new Uint8Array(n);
    
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }
    
    return new Blob([u8arr], { type: mime });
  };
  // 图片转Base64（可选，需要异步处理）
  async function convertImageToBase64(img) {
    try {
      const response = await fetch(img.src);
      const blob = await response.blob();
      
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onloadend = () => {
          img.src = reader.result; // 设置为Base64格式
          resolve();
        };
        reader.onerror = reject;
        reader.readAsDataURL(blob);
      });
    } catch (error) {
      console.error('图片转换失败:', error);
      // 转换失败时保持原URL
    }
  }

</script>

<style scoped>
.top-controls {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-bottom: 20px;
}

.export-btn {
  padding: 8px 16px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.export-btn:hover {
  background: #0056b3;
  text-decoration: none;
}

.container {
  padding: 20px;
}

.main-content {
  display: flex;
  margin-top: 20px;
  gap: 20px;
}

.left-panel {
  flex: 1;
  max-width: 300px;
  border-right: 1px solid #ccc;
  padding-right: 20px;
}

.right-panel {
  flex: 3;
}

button {
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  color: #007bff;
  margin: 5px 0;
}

button:hover {
  text-decoration: underline;
}

ul {
  list-style-type: none;
  padding-left: 20px;
}

.toolbar {
  border: 1px solid #e8e8e8;
  border-radius: 4px;
  margin-bottom: 10px;
}

.editor {
  border: 1px solid #e8e8e8;
  border-radius: 4px;
  height: 500px;
  overflow: auto;
  padding: 15px;
}
</style>

<style>
#editor {
  border: 1px solid #e8e8e8;
  border-radius: 4px;
  height: 500px;
  overflow: auto;
  padding: 15px;
}
#A{
  display: none;
}
/* 全局样式确保v-html中的表格正确显示 */
.right-panel table {
  border-collapse: collapse;
  width: 100%;
  margin: 10px 0;
}
.right-panel td, .right-panel th {
  border: 1px solid #ddd;
  padding: 8px;
}
.right-panel tr:nth-child(even) {
  background-color: #f2f2f2;
}
  .right-panel p {
    margin: 10px 0;
    line-height: 1.6;
    font-size: 16px;
  }
  .custom-tree-node {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;
    padding-right: 8px;
  }
  .ql-mention-value {
    color: blue;
  }
</style>